package com.lx.constants.aspect;

import com.google.common.util.concurrent.RateLimiter;
import com.lx.constants.CommonConstants;
import com.lx.constants.aop.ExRateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/***
 * create by lanyxp on 2020/6/25 0025
 * 该方法执行服务降级切面,该切面整合guava的rateLimiter
 * RateLimiter基于令牌桶算法实现限流  作用于接口
 * @see RateLimiter
 */
@Aspect
@Component
@Slf4j
public class ExRateLimiterAspect {
    /***
     * 存放接口是否已经存在 线程安全
     */
    private static final ConcurrentHashMap<String, RateLimiter> rateLimiterMap = new ConcurrentHashMap<String, RateLimiter>();

    @Pointcut("execution(public * com.lx.app.controller.*.*(..))")
    public void ExRateLimiter() {
    }

    @Around("ExRateLimiter()")
    public Object doBefore(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        ExRateLimiter extRateLimiter = signature.getMethod().getDeclaredAnnotation(ExRateLimiter.class);
        if (extRateLimiter == null) {
            Object proceed = proceedingJoinPoint.proceed();
            return proceed;
        }
        double value = extRateLimiter.value();
        long timeOut = extRateLimiter.timeOut();
        RateLimiter rateLimiter = getRateLimiter(value, timeOut);
        boolean tryAcquire = rateLimiter.tryAcquire(timeOut, TimeUnit.MILLISECONDS);
        if (!tryAcquire) {
            serviceDowng();
            return null;
        }
        Object proceed = proceedingJoinPoint.proceed();
        return proceed;
    }

    /***
     * @param value 配置固定速率
     * @param timeOut 获取等待令牌等待时间 毫秒
     * @return RateLimiter
     * @see RateLimiter
     */
    private RateLimiter getRateLimiter(double value, long timeOut) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String requestURI = request.getRequestURI();
        RateLimiter rateLimiter = null;
        if (!rateLimiterMap.containsKey(requestURI)) {
            rateLimiter = RateLimiter.create(value);
            rateLimiterMap.put(requestURI, rateLimiter);
        } else {
            rateLimiter = rateLimiterMap.get(requestURI);
        }
        return rateLimiter;
    }

    /***
     *服务降级策略 输出友好提示给客户端
     * @throws IOException
     */
    private void serviceDowng() throws IOException {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = attributes.getResponse();
        response.setHeader("Content-type", "text/html;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        try {
            writer.println(CommonConstants.SYSTEM_ERROR);
        } catch (Exception e) {
            log.error("返回失败 系统端:{}", e);
        } finally {
            writer.close();
        }
    }
}